/*
fuzzer by Justin barrick

Obviously any parameters (port number, program, fuzz string) can (should) be changed.

TODO:
    * Fix process attachment

Compile:
    gcc -ofuzzer fuzzer.c -std=c99

To Use:
    ./fuzzer <-c/-s (run as client, run as server> <port> <protocol file> <(optional) IP address of server if in -c mode>

*/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>

void usage(char argv[1024])
{
    printf("Usage: %s <-c/-s (run as client, run as server> <port> <protocol file> <fuzz case file> <(optional) IP address of server if in -c mode>\n",argv);
    exit(0);
}


void *charParse(char toParse[1024], int *pos)
{
    int counter=1;
    for(int i=0;i<strlen(toParse);i++)
    {
        if(toParse[i] == '[')
        {
            pos[counter] = i;
            counter++;
            for(;i<strlen(toParse);i++)
            {
                if(toParse[i] == ']')
                {
                    pos[counter] = i;
                    counter++;
                    break;
                }
            }
        }
    }
    pos[0] = counter;
}

int servCreateSock(int port)
{
    struct sockaddr_in server_addr, client_addr;
    int true=1,sin_size;
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        perror("Creating socket failed.\n");
        exit(0);
    }
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&true,sizeof(int));

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(server_addr.sin_zero),8);

    if(bind(sock,(struct sockaddr *)&server_addr,sizeof(struct sockaddr)) < 0)
    {
        perror("Binding socket failed.\n");
        exit(0);
    }
    listen(sock, 5);

    sin_size = sizeof(client_addr);

    int accepted = accept(sock,(struct sockaddr *)&client_addr,&sin_size);
    if (accepted < 0)
    {
        perror("Accepting connection failed.\n");
        exit(0);
    }
    return accepted;
}

int clientCreateSock(int port,char addr[1024])
{
    struct sockaddr_in address;
    int sock;

    memset((void *) &address, 0, sizeof(struct sockaddr_in));
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr(addr);
    address.sin_port = htons(port); 

    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    bind(sock, (const struct sockaddr *) &address, sizeof(address));
    connect(sock,(const struct sockaddr *)&address,sizeof(address));

    return sock;
}

void fuzz(char *staticPart1, char *staticPart2, int target, int procMon, char fuzzCaseFile[1024])
{
    // Open fuzzcases
    FILE *fuzzStrings = fopen(fuzzCaseFile,"r");
    char buffer[50000],stop[10],fuzzcase[10000];
    int counter = 0;

    // For each line build the string based on the mutation string then send, wait for a connect back from the debugger and exit, otherwise move on.
    while(fgets(fuzzcase,sizeof(fuzzcase),fuzzStrings))
    {
        counter++;
        fuzzcase[strlen(fuzzcase)-1] = '\0';
        if(staticPart1 == NULL)
            strcpy(buffer,"");
        else
            strcpy(buffer,staticPart1);
        strcat(buffer,fuzzcase);
        if(staticPart2 != NULL)
            strcat(buffer,staticPart2);
        strcat(buffer,"\r\n");
        send(target,buffer,strlen(buffer),0);

        if(recv(procMon,stop,strlen(stop),0) != 0)
        {
            printf("    [debug] received: %s\n",stop);
//            exit(0);
           }
    }
    fclose(fuzzStrings);
}

int main(int argc, char *argv[])
{
    printf("fuzzer (c)2011 Justin Barrick\n\n");
    if(argc < 5)
        usage(argv[0]);

    srand(time(NULL));
    printf("Fuzzing started.\n");

    FILE *protoDesc = fopen(argv[3],"r");
    struct timeval tv;
    char mutationString[1024],staticPart1[1024],staticPart2[1024],fuzzCaseFile[1024];
    int citizenPatrol,connected,initSockM,initSockC,pos[512],count=0,count2=0;

    strcpy(fuzzCaseFile,argv[4]);
    // Wait for citizenPatrol.py to establish connection, then connect to the target program

    citizenPatrol = servCreateSock(2444);
    if(strcmp(argv[1],"-s") == 0)
    {
        if(argc != 5)
            usage(argv[0]);
        connected = servCreateSock(atoi(argv[2]));
    }
    else
    {
        if(argc != 6)
            usage(argv[0]);
        connected = clientCreateSock(atoi(argv[2]),argv[4]);
    }

    // Set citizenPatrol.py wait timeout to 1 second

    tv.tv_sec  = 1;
    tv.tv_usec = 0;
    setsockopt(citizenPatrol, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

    strcpy(mutationString,"");
    bzero((void*)pos,sizeof(pos));
    strcpy(staticPart1,"\0");
    strcpy(staticPart2,"\0");

    while (fgets(mutationString,sizeof(mutationString),protoDesc))
    {
        // Parse the line, outputs the position of the brackets
        charParse(mutationString,pos);

        mutationString[strlen(mutationString)-1] = '\0';
        printf("  [*] Current mutation string: %s\n", mutationString);

        // Split the mutation string and execute crapShoot()
        if(pos[1] == 0)
            strcpy(staticPart1,NULL);
        else
        {
            for(;count<pos[1];count++)
                staticPart1[count] = mutationString[count];
        }
        if(pos[2]+1 == strlen(mutationString))
        {
            fuzz(staticPart1,NULL,connected,citizenPatrol,fuzzCaseFile);
        }
        else
        {
            count = pos[2] + 1;
            for(;count<strlen(mutationString);count++)
            {
                staticPart2[count2] = mutationString[count];
                count2++;
            }
            fuzz(staticPart1,staticPart2,connected,citizenPatrol,fuzzCaseFile);
        }

        strcpy(mutationString,"");
        bzero((void*)pos,sizeof(pos));
        strcpy(staticPart1,"\0");
        strcpy(staticPart2,"\0");
    }
    printf("Fuzzing session completed with no crashes.\n");
    return 0;
}
